home *** CD-ROM | disk | FTP | other *** search
/ ...taking it to the Macs! / ...taking it to the Macs!.iso / Extras / ActiveX Mac SDK / ActiveX SDK / Sample Controls / Console / CConsoleControl.cpp next >
Encoding:
C/C++ Source or Header  |  1996-12-21  |  14.0 KB  |  577 lines  |  [TEXT/CWIE]

  1. #include "ocheaders.h"
  2. #include <stdio.h>
  3. #include <ctype.h>
  4. #include "BDDISPIDs.h"
  5. #include "CBaseControl.h"
  6. #include "CConsoleControl.h"
  7. #include "BDConsts.h"
  8. #include "BDUtils.h"
  9. #include <LArray.h>
  10. #include "CConnectionPoint.h"
  11. #include "CCPContainer.h"
  12.  
  13.  
  14. ///////////////////////////////////////////////////////////////////////////////
  15. //
  16. //  CConsoleControl::CConsoleControl
  17. //
  18.  
  19. CConsoleControl::CConsoleControl(void) :
  20.     CBaseControl()
  21. {
  22.     m_Cookie = 0;
  23.     m_Message = NULL;
  24.     m_NumMsgs = 0;
  25.     m_CurrentMsg = 0;
  26.  
  27.     mClickSource = NULL;
  28.  
  29.     mIsIdling = false;
  30.     
  31.     mID[0] = 0;
  32.  
  33.     CConnectionPoint*    connectionPoint;
  34.     IConnectionPoint*    cp;
  35.     short                i;
  36.     CCPContainer*        containerObj = nil;
  37.  
  38.     // Create the new connection point container object
  39.     containerObj = new CCPContainer(NUM_CONNECTIONS);
  40.     
  41.     // Snag the interface pointer
  42.     if ( containerObj )
  43.         containerObj->QueryInterface(IID_IConnectionPointContainer, &mCPContainerP);
  44.     
  45.     // if we have a container object, allocate the connection points
  46.     if ( mCPContainerP )
  47.         // We support 1 connection point
  48.         containerObj->AddConnectionPoint(IID_IDoMenuEvents);
  49. }
  50.  
  51. ///////////////////////////////////////////////////////////////////////////////
  52. //
  53. //  CConsoleControl::~CConsoleControl
  54. //
  55.  
  56. CConsoleControl::~CConsoleControl()
  57. {
  58.     if ( mIsIdling )
  59.         mContainerSiteP->SetIdleTime(RemoveAllIdlers, 0);
  60.         
  61.     if (m_Message)
  62.     {
  63.         DisposeHandle((Handle) m_Message);
  64.         m_Message = NULL;
  65.     }
  66.     
  67. }
  68.  
  69. ///////////////////////////////////////////////////////////////////////////////
  70. //
  71. //  CConsoleControl::IUnknown::QueryInterface
  72. //
  73. //  Returns a pointer to the specified interface on a component to which a
  74. //  client currently holds an interface pointer.
  75. //
  76.  
  77. STDMETHODIMP
  78. CConsoleControl::QueryInterface(REFIID RefID, void** Obj)
  79. {
  80.     if ( RefID == IID_IDidMenuEvents ) // an incoming interface
  81.     {
  82.         *Obj = (void*) (IDidMenuEvents*) this;
  83.         AddRef();
  84.         
  85.         return ResultFromScode(S_OK);
  86.     }
  87.     else
  88.         return CBaseControl::QueryInterface(RefID, Obj);
  89. }
  90.  
  91.  
  92. ///////////////////////////////////////////////////////////////////////////////
  93. //
  94. //  CConsoleControl::IObjectWithSite::SetSite
  95. //
  96. STDMETHODIMP
  97. CConsoleControl::SetSite(IUnknown* inClientSite)
  98. {
  99.     CBaseControl::SetSite(inClientSite);
  100.     
  101.     if ( mContainerSiteP )
  102.         mIsIdling = ( mContainerSiteP->SetIdleTime(0, 0) == S_OK );
  103. }
  104.  
  105.  
  106. ///////////////////////////////////////////////////////////////////////////////
  107. //
  108. //  CConsoleControl::IControl::Draw
  109. //
  110.  
  111. STDMETHODIMP
  112. CConsoleControl::Draw(DrawContext* Context)
  113. {
  114.     FontInfo    fInfo;
  115.     short        curX;
  116.     short        curY;
  117.     short        i;
  118.     short        msgIndex;
  119.     char        theState;
  120.     RgnHandle    saveClipRgn;
  121.     Rect        controlRect, newClipRect;
  122.     Boolean        overlap = false;
  123.     
  124.     if (Context->DrawAspect != DVASPECT_CONTENT)
  125.         return DV_E_DVASPECT;
  126.  
  127.     // Set the font and size    
  128.     ::TextFont(applFont);
  129.     ::TextFace(0);
  130.     ::TextMode(srcCopy);
  131.     ::TextSize(9);
  132.     ::GetFontInfo(&fInfo);
  133.  
  134.     // Erase the area and outline the frame
  135.     ::EraseRect(&Context->Location);
  136.     ::PenSize(1, 1);
  137.     ::FrameRect(&Context->Location);
  138.  
  139.     // Save the current clip
  140.     saveClipRgn = ::NewRgn();
  141.     ::GetClip(saveClipRgn);
  142.  
  143.     controlRect = Context->Location;
  144.     ::InsetRect(&controlRect, 3, 3);
  145.     overlap = SectRect(&controlRect, &((*saveClipRgn)->rgnBBox), &newClipRect);
  146.  
  147.     if (overlap)
  148.     {
  149.         // Reset the clip
  150.         ::ClipRect(&newClipRect);
  151.     }
  152.  
  153.     // Initialize the message storage, if not already done
  154.     if (!m_Message) {
  155.         InitMessages(Context);
  156.     }
  157.     
  158.     curX = Context->Location.left+3;
  159.     curY = Context->Location.top+3;
  160.  
  161.     // Draw the messages, one on each line    
  162.     curY += fInfo.ascent;
  163.     
  164.     msgIndex = (m_CurrentMsg + 1) % m_NumMsgs;
  165.     theState = ::HGetState((Handle) m_Message);
  166.     ::HLock((Handle) m_Message);
  167.  
  168.     for (i = 1; i <= m_NumMsgs; i++)
  169.     {
  170.         ::MoveTo(curX, curY);
  171.         ::DrawString(*(*m_Message + msgIndex));
  172.         curY += fInfo.ascent + fInfo.descent + fInfo.leading;
  173.         msgIndex = (msgIndex + 1) % m_NumMsgs;
  174.     }
  175.  
  176.     ::HSetState((Handle) m_Message, theState);
  177.  
  178.     if (overlap)
  179.     {
  180.         // Restore the clip
  181.         ::SetClip(saveClipRgn);
  182.     }
  183.     ::DisposeRgn(saveClipRgn);
  184.                 
  185.     return S_OK;
  186. }
  187.  
  188. ///////////////////////////////////////////////////////////////////////////////
  189. //
  190. //  CConsoleControl::IControl::OnEvent
  191. //
  192.  
  193. STDMETHODIMP
  194. CConsoleControl::DoIdle(Uint32 IdleRefCon)
  195. {
  196.     // if the client site has been set and the advisory cookie has not,
  197.     // enumerate the other controls in this container to find the one we want
  198.     // to connect to
  199.     if (mContainerSiteP != NULL && m_Cookie == 0)
  200.     {
  201.         IUnknown*         testControl;
  202.         IEnumUnknown*    enumU;
  203.     
  204.         if ( !mContainerP )
  205.         {
  206.             mContainerSiteP->GetContainer(&mContainerP);
  207.             if ( mContainerP )
  208.                 mContainerP->AddRef();
  209.             else
  210.                 return S_OK;
  211.         }
  212.         
  213.         // Enumerate the objects
  214.         if ( SUCCEEDED(mContainerP->EnumControls(nil, OLECONTF_EMBEDDINGS, &enumU)) )
  215.         {
  216.             IConnectionPointContainer* cpContainer = nil;
  217.             
  218.             while ( enumU->Next(1, &testControl, nil) == NOERROR )
  219.             {
  220.                 // Try to get a connection point container from the control
  221.                 testControl->QueryInterface(IID_IConnectionPointContainer,  (void**) &cpContainer);
  222.                 
  223.                 // if it has one, this may be it
  224.                 if ( cpContainer )
  225.                 {
  226.                     IConnectionPoint* cp = nil;
  227.                     
  228.                     // See if this connection point container 
  229.                     // has a connection point with the outgoing interface we want
  230.                     cpContainer->FindConnectionPoint(IID_IDidMenuEvents, &cp);
  231.                 
  232.                     // if we got it, set an advise connection on the outgoing interface
  233.                     if ( cp )
  234.                     {
  235.                         IUnknown* unk;
  236.                         
  237.                         this->QueryInterface(IID_IUnknown, (void**) &unk);
  238.                         cp->Advise(unk, &m_Cookie);
  239.                     }
  240.                 }
  241.             }
  242.             
  243.             // Don't forget to release
  244.             enumU->Release();
  245.          }
  246.          else
  247.              m_Cookie = -1;
  248.     }
  249.     
  250.     // if we're hooked up, discontinue idling
  251.     if ( m_Cookie != 0 )
  252.     {
  253.         mContainerSiteP->SetIdleTime(RemoveAllIdlers, 0);
  254.         mIsIdling = false;
  255.     }
  256.  
  257.     return S_OK;
  258. }
  259.  
  260.  
  261. ///////////////////////////////////////////////////////////////////////////////
  262. //
  263. //  CConsoleControl::IPersistPropertyBag::Load
  264. //
  265.  
  266. STDMETHODIMP
  267. CConsoleControl::Load(IPropertyBag* propertyBag, IErrorLog* errorLog)
  268. {
  269.     CBaseControl::Load(propertyBag, errorLog);
  270.  
  271.     char        propertyString[Str255BufferLength];
  272.  
  273.     if ( ::LoadPropertyString(propertyBag, "sourceid", propertyString, Str255StringLength, errorLog) )
  274.     {
  275.         strcpy((char*)(&mID[1]), propertyString);
  276.         mID[0] = strlen(propertyString);
  277.     }
  278.     
  279.     return ResultFromScode(S_OK);
  280. }
  281.  
  282.  
  283. ///////////////////////////////////////////////////////////////////////////////
  284. //
  285. //  CConsoleControl::IDidMenuEvents::Click
  286. //
  287.  
  288. STDMETHODIMP
  289. CConsoleControl::Click(IUnknown* Source, PlatformEvent* Event, char * originatorName, const CMenuItem & Item)
  290. {
  291.     Str255            theMessage;
  292.     
  293.     // Add message to tell which object (button) caused the popup, which item in the popup
  294.     // was clicked, and the text of that item.    
  295.     
  296.     sprintf((char *)theMessage, "%s%s%s%s%s%ld%s%s%s%s",
  297.         "Received Popup Click Event:",
  298.         " Originator = '", originatorName, "', ",
  299.         " Menu item number = ", Item.itemNumber, ", ",
  300.         " Menu item text = '", Item.itemText, "'");
  301. //    sprintf((char *)theMessage, "%s, item number %d: %s", originatorName, Item.itemNumber, Item.itemText);
  302.     c2pstr((char *)theMessage);
  303.     AddMessage(theMessage);
  304.     
  305.     // Parse the text of the selected menu item to determinw whether to Append 
  306.     // a menu item, remove the last one, or clear the whole menu.
  307.     
  308.     Boolean fire = false;
  309.     long eventID = ParseMenuItem(Item);
  310.     
  311.     switch ( eventID )
  312.     {
  313.         case DISPID_CLEAR:
  314.         {
  315.             sprintf((char *)theMessage, "Firing event to clear '%s' popup", originatorName);
  316.             c2pstr((char *)theMessage);
  317.             AddMessage(theMessage);
  318.  
  319.             fire = true;
  320.  
  321.             break;
  322.         }
  323.             
  324.         case DISPID_REMOVEITEM:
  325.         {
  326.             mTempMenuItem.itemNumber = Item.itemNumber;
  327.             strcpy(mTempMenuItem.itemText, Item.itemText); 
  328.  
  329.             sprintf((char *)theMessage, "Firing event to remove menu item number %d in '%s' popup", Item.itemNumber, originatorName);
  330.             c2pstr((char *)theMessage);
  331.             AddMessage(theMessage);
  332.             
  333.             fire = true;
  334.             
  335.             break;
  336.         }
  337.             
  338.         case DISPID_ADDITEM:
  339.         {
  340.             char itemText[256];
  341.             sprintf(itemText, "This item, number %ld, created by Console", Item.itemNumber);
  342.             
  343.             mTempMenuItem.itemNumber = Item.itemNumber;
  344.             strcpy(mTempMenuItem.itemText, itemText); 
  345.  
  346.             sprintf((char *)theMessage, "Firing event to add menu item number %d in '%s' popup", Item.itemNumber, originatorName);
  347.             c2pstr((char *)theMessage);
  348.             AddMessage(theMessage);
  349.             
  350.             fire = true;
  351.             
  352.             break;
  353.         }
  354.         
  355.         default:
  356.             break;
  357.     }
  358.     
  359.     if ( fire )
  360.     {
  361.         // note which control sent us the Click message in the first place, 
  362.         // because that's the only one we want to fire an event at in return.
  363.         if ( SUCCEEDED( Source->QueryInterface(IID_IDoMenuEvents, (void**) &mClickSource) ))        
  364.             FireEvent(IID_IDoMenuEvents, eventID, Event);
  365.     }
  366.     
  367.     FinishEvent();
  368.     
  369.     return ResultFromScode(S_OK);
  370. }
  371.  
  372. ///////////////////////////////////////////////////////////////////////////////
  373. //
  374. //  CConsoleControl::FireEvent
  375. //
  376.  
  377. STDMETHODIMP
  378. CConsoleControl::FireEvent(REFIID RefID, long EventID, PlatformEvent* Event)
  379. {
  380.     IEnumConnectionPoints*    enumCP;
  381.     IEnumConnections*        enumC;
  382.     CONNECTDATA                connectData;
  383.     IUnknown*                eventTarget;
  384.     IConnectionPoint*        connectionPoint;
  385.     
  386.     // Get an enumerator for the connection points
  387.     if ( SUCCEEDED(mCPContainerP->EnumConnectionPoints(&enumCP)) )
  388.     {
  389.         // Loop through all the connection points for this control
  390.         while ( enumCP->Next(1, &connectionPoint, nil) == NOERROR )
  391.         {
  392.             // Get all the connections for this connection point
  393.             if ( SUCCEEDED( connectionPoint->EnumConnections(&enumC) ))
  394.             {
  395.                 // Loop through all the connections for this connection point    
  396.                 while ( enumC->Next(1, &connectData, nil) == NOERROR )
  397.                 {
  398.                     // Get the interface implementation for this connection
  399.                     // if successful, fire the event
  400.                     if ( SUCCEEDED( connectData.pUnk->QueryInterface(RefID, (void**) &eventTarget) ))
  401.                         FireOneEvent(RefID, EventID, eventTarget, Event);
  402.                 }
  403.                 
  404.                 // Release the enumerator
  405.                 enumC->Release();
  406.             }
  407.         }
  408.         
  409.         enumCP->Release();
  410.     }
  411.     
  412.     return ResultFromScode(S_OK);
  413. }
  414.  
  415. ///////////////////////////////////////////////////////////////////////////////
  416. //
  417. //  CConsoleControl::FireOneEvent
  418. //
  419.  
  420. STDMETHODIMP
  421. CConsoleControl::FireOneEvent(REFIID RefID, long EventID, IUnknown* EventTarget, PlatformEvent* Event)
  422. {
  423.     IDoMenuEvents*        popTarget = (IDoMenuEvents*) EventTarget;
  424.     IUnknown*            unk;
  425.     
  426.     // right now, we're only concerned with firing events back at objects that
  427.     // sent us a Click message.
  428.     if ( mClickSource )
  429.     {
  430.         if ( EventTarget == mClickSource )
  431.         {
  432.             this->QueryInterface(IID_IUnknown, (void**) &unk);
  433.             
  434.             switch ( EventID )
  435.             {
  436.                 case DISPID_CLEAR:
  437.                     popTarget->Clear(unk, Event);
  438.                     break;
  439.                     
  440.                 case DISPID_REMOVEITEM:
  441.                     popTarget->RemoveItem(unk, Event, mTempMenuItem);
  442.                     break;
  443.                     
  444.                 case DISPID_ADDITEM:
  445.                     popTarget->AddItem(unk, Event, mTempMenuItem);
  446.                     break;
  447.             }
  448.             
  449.             mClickSource = NULL; // reset for next event
  450.         }
  451.     }
  452.     else // no mClickSource -- handle unspecific messages
  453.         ; // none yet
  454.     
  455.     return ResultFromScode(S_OK);
  456. }
  457.  
  458. ///////////////////////////////////////////////////////////////////////////////
  459. //
  460. //  CConsoleControl::FinishEvent
  461. //
  462.  
  463. void CConsoleControl::FinishEvent(void)
  464. {
  465.     InvalAllContexts();
  466. }
  467.  
  468. ///////////////////////////////////////////////////////////////////////////////
  469. //
  470. //  CConsoleControl::AddMessage
  471. //
  472.  
  473. void CConsoleControl::AddMessage(Str255 inMessage)
  474. {
  475.     char    theState;
  476.  
  477.     if (!m_Message)
  478.         InitMessages(nil);
  479.  
  480.     // Lock the message storage    
  481.     theState = ::HGetState((Handle) m_Message);
  482.     ::HLock((Handle) m_Message);
  483.  
  484.     // Advance the message pointer    
  485.     m_CurrentMsg++;
  486.     if (m_CurrentMsg >= m_NumMsgs)
  487.         m_CurrentMsg = 0;
  488.  
  489.     // Copy the message into the message storage
  490.     ::PLstrcpy(*(*m_Message + m_CurrentMsg), inMessage);
  491.     
  492.     // Unlock the message storage
  493.     ::HSetState((Handle) m_Message, theState);
  494. }
  495.  
  496. ///////////////////////////////////////////////////////////////////////////////
  497. //
  498. //    CConsoleControl::InitMessages
  499. //
  500.  
  501. void CConsoleControl::InitMessages(DrawContext* inContext)
  502. {
  503.     // Initialize the message storage, if not already done
  504.     if (!m_Message) 
  505.     {
  506.         DrawContext    Context = {(PortType) 0};
  507.         short        height = 0;
  508.         FontInfo    fInfo;
  509.         long        index = 1;
  510.         ErrorCode    Result = S_OK;
  511.  
  512.         if ( inContext )
  513.             Context = *inContext;
  514.         else
  515.             Result = mContainerSiteP->AcquireContext(mActiveContext->GetContextID(), &Context);
  516.             
  517.         if ( Result == S_OK )
  518.         {
  519.             // Set the font and size    
  520.             ::TextFont(applFont);
  521.             ::TextFace(0);
  522.             ::TextSize(9);
  523.             ::GetFontInfo(&fInfo);
  524.  
  525.             // Figure the number of lines that can be displayed in the control        
  526.             height = Context.Location.bottom - Context.Location.top - 6;
  527.             m_NumMsgs = height/(fInfo.ascent + fInfo.descent + fInfo.leading);
  528.  
  529.             // Add one more line if the leading is the only part that won't fit
  530.             if (height - m_NumMsgs * (fInfo.ascent + fInfo.descent + fInfo.leading) >= 
  531.                 (fInfo.ascent + fInfo.descent)) {
  532.                 m_NumMsgs++;
  533.             }
  534.             
  535.             // Allocate the memory
  536.             if (m_NumMsgs > 0)
  537.             {
  538.                 m_Message = (Str255**) ::NewHandleClear(sizeof(Str255) * m_NumMsgs);
  539.                 m_CurrentMsg = m_NumMsgs ? m_NumMsgs - 1: 0;
  540.             }
  541.             else
  542.                 m_NumMsgs = 0;
  543.  
  544.             if ( !inContext )
  545.                 mContainerSiteP->ReleaseContext(&Context);
  546.         }
  547.     }
  548. }
  549.  
  550. ///////////////////////////////////////////////////////////////////////////////
  551. //
  552. //  CConsoleControl::ParseMenuItem
  553. //
  554.  
  555. long CConsoleControl::ParseMenuItem(const CMenuItem & item)
  556. {
  557.     long id = 0;
  558.  
  559.     // get the leading characters, in lowercase. kLeadingLength is set
  560.     // big enough to accommodate the strings we're looking for.
  561.     const short kLeadingLength = 16;
  562.     char leadingChars[kLeadingLength+1];
  563.     long itemTextLength = strlen(item.itemText);
  564.     strncpy(leadingChars, item.itemText, (itemTextLength < kLeadingLength ? itemTextLength : kLeadingLength));
  565.     for ( short i = 0; i < strlen(leadingChars); i++ )
  566.         leadingChars[i] = tolower(leadingChars[i]);
  567.     
  568.     if ( strstr(leadingChars, "add") == leadingChars ) // does the string start with "add" ?
  569.         id = DISPID_ADDITEM;
  570.     else if ( strstr(leadingChars, "remove") == leadingChars ) // does the string start with "add" ?
  571.         id = DISPID_REMOVEITEM;
  572.     else if ( strstr(leadingChars, "clear") == leadingChars ) // does the string start with "add" ?
  573.         id = DISPID_CLEAR;
  574.         
  575.     return id;
  576. }
  577.